console.log("--------------2.1 ES6 let 与 const----------------");
(function () {

    // let 声明的变量只在 let 命令所在的代码块内有效。
    var a = [];
    for (let i = 0; i < 10; i++) {
        a[i] = function () {
            console.log(i);
        };
    }
    a[6](); // 6

    // const 声明一个只读的常量，一旦声明，常量的值就不能改变。
    const PI = 3.1415;
    console.log(PI); // 3.1415
    // PI = 3;  // TypeError: Assignment to constant variable.

    const foo = {};
    // 为 foo 添加一个属性，可以成功
    foo.prop = 123;
    console.log(foo.prop); // 123

    // 将 foo 指向另一个对象，就会报错
    //foo = {}; // TypeError: "foo" is read-only
})();


console.log("--------------2.2 ES6 解构赋值----------------");
(function () {

    // 解构赋值是对赋值运算符的扩展
    let [a, b, c] = [1, 2, 3];
    console.log(a + " " + b + " " + c);

    // 剩余运算符
    let [d, ...e] = [1, 2, 3]; //d = 1, e = [2, 3]
    console.log(d + " " + e);

    // 默认值
    let [x, y = 'b'] = ['a', undefined]; // x='a', y='b'
    console.log(x + " " + y);

    let {foo, bar} = {foo: 'aaa', bar: 'bbb'}; // foo = 'aaa' ,bar = 'bbb'
    console.log(foo + " " + bar);
})();


console.log("--------------2.3 ES6 Symbol----------------");
(function () {

    // ES6 引入了一种新的原始数据类型 Symbol ，表示独一无二的值，最大的用法是用来定义对象的唯一属性名。
    let sy = Symbol("KK");
    console.log(sy);   // Symbol(KK)
    console.log(typeof (sy));   // "symbol"

    // 相同参数 Symbol() 返回的值不相等
    let sy1 = Symbol("KK");
    console.log(sy === sy1);    // false

    // 作为属性值
    let myKey = Symbol("myKey");
    let syObject = {};
    syObject[myKey] = "k1"; // Symbol 作为对象属性名时不能用.运算符，要用方括号
    syObject.myKey = "k2";
    console.log(syObject);    // {myKey: "k2", Symbol(myKey): "k1"}

    // 获取属性
    console.log(Object.getOwnPropertySymbols(syObject));    // [Symbol(myKey)]
    console.log(Reflect.ownKeys(syObject));                 // ["myKey", Symbol(myKey)]

    // 保证switch不会出现重复常量
    const COLOR_RED = Symbol("red");
    const COLOR_YELLOW = Symbol("yellow");
    const COLOR_BLUE = Symbol("blue");

    function getConstantName(color) {
        switch (color) {
            case COLOR_RED :
                return "COLOR_RED";
            case COLOR_YELLOW :
                return "COLOR_YELLOW ";
            case COLOR_BLUE:
                return "COLOR_BLUE";
            default:
                throw new Exception('Cant find this color');
        }
    }

    console.log(getConstantName(COLOR_YELLOW));

    // Symbol.for() 单例模式，保证全局仅有一个相同名称的Symbol
    function getSingleConstantName(color) {
        color = Symbol.for(color);
        switch (color) {
            case Symbol.for("red") :
                return "COLOR_RED";
            case Symbol.for("yellow") :
                return "COLOR_YELLOW ";
            case Symbol.for("blue"):
                return "COLOR_BLUE";
            default:
                return "OTHER";
        }
    }

    console.log(getSingleConstantName("yellow"));

    // Symbol.keyFor() 用来检测该字符串参数作为名称的 Symbol 值是否已被登记。
    let green = Symbol.for("green");
    console.log(Symbol.keyFor(green));    // "green"
    console.log(Symbol.keyFor(Symbol("testNoExist")));    // undefined

})();

console.log("--------------3.1.1 ES6 Map 与 Set----------------");
(function () {
    // Map 对象保存键值对。
    //  Map 的键可以是任意值，Map 中的键值是有序的（FIFO 原则），Map 的键值对个数可以从 size 属性获取
    let myMap = new Map();

    let keyString = "a string";
    myMap.set(keyString, "和键'a string'关联的值");
    console.log(myMap.get(keyString));    // "和键'a string'关联的值"
    console.log(myMap.get("a string"));   // "和键'a string'关联的值"

    let keyObj = {}; // 对象
    myMap.set(keyObj, "和键 keyObj 关联的值");
    console.log(myMap.get(keyObj)); // "和键 keyObj 关联的值"
    console.log(myMap.get({})); // undefined, 因为 keyObj !== {}

    let keyFunc = function () {
    }; // 函数
    myMap.set(keyFunc, "和键 keyFunc 关联的值");
    console.log(myMap.get(keyFunc)); // "和键 keyFunc 关联的值"
    console.log(myMap.get(function () {
    })); // undefined, 因为 keyFunc !== function () {}

    myMap.set(NaN, "not a number");
    console.log(myMap.get(NaN)); // "not a number"
    let otherNaN = Number("foo");
    console.log(myMap.get(otherNaN)); // "not a number"
})();

console.log("--------------3.1.2 ES6 Reflect 与 Proxy----------------");
(function () {

    // Proxy 可以对目标对象的读取、函数调用等操作进行拦截，然后进行操作处理。
    // 它不直接操作对象，而是像代理模式，通过对象的代理对象进行操作，在进行这些操作时，可以添加一些需要的额外操作。
    // 与 Object.defineProperty() 对比，Proxy可以直接监听对象而非属性，可以直接监听数组的变化，功能更加强大
    let target = {
        name: 'Tom',
        age: 24
    };
    let handler = {
        get: function (target, key) {
            console.log('getting ' + key);
            return target[key]; // 不是target.key
        },
        set: function (target, key, value) {
            console.log('setting ' + key);
            target[key] = value;
            return true;   // 需要加返回值，否则报错：TypeError: 'set' on proxy: trap returned falsish for property 'age'
        }
    };
    let proxy = new Proxy(target, handler);
    console.log(proxy.name);    // 实际执行 handler.get
    // getting name
    // Tom
    proxy.age = 25;  // 实际执行 handler.set
    // setting age
    console.log(proxy.age);
    // 25

    // target 可以为空对象
    let targetEpt = {};
    let proxyEpt = new Proxy(targetEpt, handler);
    // 调用 get 方法，此时目标对象为空，没有 name 属性
    console.log(proxyEpt.name); // getting name  undefined
    // 调用 set 方法，向目标对象中添加了 name 属性
    proxyEpt.name = 'Tom'; // setting name
    // 再次调用 get ，此时已经存在 name 属性
    console.log(proxyEpt.name);
    // getting name
    // Tom

    // 通过构造函数新建实例时其实是对目标对象进行了浅拷贝，因此目标对象与代理对象会互相影响
    console.log(proxyEpt);
    console.log(targetEpt);
    // {name: "Tom"}

    // handler 对象也可以为空，相当于不设置拦截操作，直接访问目标对象
    let targetEmpty = {};
    let proxyEmpty = new Proxy(targetEmpty, {});
    proxyEmpty.name = "Tom";
    console.log(proxyEmpty);
    console.log(targetEmpty);
    // {name: "Tom"}

    // apply(target, ctx, args) 用于拦截函数的调用、call 和 reply 操作
    function sub(a, b) {
        return a - b;
    }

    let handlerApply = {
        apply: function (target, ctx, args) {
            console.log('handle apply');
            return Reflect.apply(...arguments);
        }
    };
    let proxyApply = new Proxy(sub, handlerApply);
    console.log(proxyApply(2, 1));
    // handle apply
    // 1

    // has(target, propKey) 用于拦截 HasProperty 操作，即在判断 target 对象是否存在 propKey 属性时，会被这个方法拦截。
    let handlerHas = {
        has: function (target, propKey) {
            console.log("handle has");
            return propKey in target;
        }
    };
    let exam = {name: "Tom"};
    let proxyHas = new Proxy(exam, handlerHas);
    console.log('name' in proxyHas);
    // handle has
    // true

    // construct(target, args)
    // 用于拦截 new 命令。返回值必须为对象。

    // deleteProperty(target, propKey)
    // 用于拦截 delete 操作，如果这个方法抛出错误或者返回 false ，propKey 属性就无法被 delete 命令删除。
    //
    // defineProperty(target, propKey, propDesc)
    // 用于拦截 Object.definePro若目标对象不可扩展，增加目标对象上不存在的属性会报错；若属性不可写或不可配置，则不能改变这些属性。

    // getOwnPropertyDescriptor(target, propKey)
    // 用于拦截 Object.getOwnPropertyD() 返回值为属性描述对象或者 undefined 。

    // getPrototypeOf(target)
    // 主要用于拦截获取对象原型的操作。包括以下操作：- Object.prototype._proto_ - Object.prototype.isPrototypeOf() - Object.getPrototypeOf() - Reflect.getPrototypeOf() - instanceof

    // isExtensible(target)
    // 用于拦截 Object.isExtensible 操作。该方法只能返回布尔值，否则返回值会被自动转为布尔值。

    // ownKeys(target)
    // 用于拦截对象自身属性的读取操作。 主要包括以下操作：- Object.getOwnPropertyNames() - Object.getOwnPropertySymbols() - Object.keys() - or...in

    // preventExtensions(target)
    // 拦截 Object.preventExtensions 操作。

    // setPrototypeOf
    // 主要用来拦截 Object.setPrototypeOf 方法。

    // Proxy.revocable()
    // 用于返回一个可取消的 Proxy 实例。


    // Reflect 可以用于获取目标对象的行为，它与 Object 类似，但是更易读，为操作对象提供了一种更优雅的方式。它的方法与 Proxy 是对应的。
    let examGet = {
        name: "Tom",
        age: 24,
        get info(){
            return this.name + this.age;
        }
    };
    console.log(Reflect.get(examGet, 'name')); // "Tom"

    // 当 target 对象中存在 name 属性的 getter 方法， getter 方法的 this 会绑定 // receiver
    let receiver = {
        name: "Jerry",
        age: 20
    };
    console.log(Reflect.get(examGet, 'info', receiver)); // Jerry20

    // 当 name 为不存在于 target 对象的属性时，返回 undefined
    console.log(Reflect.get(examGet, 'birth')); // undefined

    // 当 target 不是对象时，会报错
    // Reflect.get(1, 'name'); // TypeError


    let examSet = {
        name: "Tom",
        age: 24,
        set info(value){
            return this.age = value;
        }
    };
    console.log(examSet.age); // 24
    Reflect.set(examSet, 'age', 25); // true
    console.log(examSet.age); // 25

    // value 为空时会将 name 属性清除
    Reflect.set(examSet, 'age', ); // true
    console.log(examSet.age); // undefined

    // 当 target 对象中存在 name 属性 setter 方法时，setter 方法中的 this 会绑定 // receiver , 所以修改的实际上是 receiver 的属性,
    let receiver1 = {
        age: 18
    };
    Reflect.set(examSet, 'info', 1, receiver1); // true
    console.log(receiver1.age); // 1

    let receiver2 = {
        name: 'oppps'
    };
    Reflect.set(examSet, 'info', 1, receiver2);
    console.log(receiver2.age); // 1

})();

console.log("--------------3.2.1 ES6 字符串----------------");
(function () {

})();

console.log("--------------3.2.2 ES6 数值----------------");
(function () {

})();

console.log("--------------3.2.3 ES6 对象----------------");
(function () {

})();

console.log("--------------3.2.4 ES6 数组----------------");
(function () {

})();